﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.ServiceProcess;
using System.Threading;
using Helpers;
using Helpers.Common;
using System.Reflection;
using Helpers.Log;
using log4net;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Net;
using System.Globalization;
using Billing.Service;
using Billing.Service.Wcf;

public class Service : ServiceBase
{
    #region Properties

    /// <summary>
    /// The name of the service to be displayed to the user.
    /// </summary>
    public string DisplayName { 
        get { return CustomInstaller.ServiceDisplayName; } 
    }
    /// <summary>
    /// An object with the service configuration.
    /// </summary>
    public ServiceConfiguration Configuration { get; private set; }
    /// <summary>
    /// Behind value or property IsShuttingDown.
    /// </summary>
    private bool isShuttingDown = false;
    /// <summary>
    /// Indicates if the service is shutting down.
    /// </summary>
    public bool IsShuttingDown
    {
        get { return this.isShuttingDown; }
        set { this.isShuttingDown = value; }
    }


    /// <summary>
    /// The name or ip of the machine where the wcf service will be hosted.
    /// </summary>
    public string WcfServiceHost {
        get { return this.Configuration.WcfServiceHost; }
        set { this.Configuration.WcfServiceHost = value; } 
    }
    /// <summary>
    /// The port where the wcf service will be hosted.
    /// </summary>
    public int WcfServicePort
    {
        get { return this.Configuration.WcfServicePort; }
        set { this.Configuration.WcfServicePort = value; }
    }
    /// <summary>
    /// The url where the wcf service will be hosted.
    /// </summary>
    public string WcfServiceUrl
    {
        get { return this.Configuration.WcfServiceUrl; }
        set { this.Configuration.WcfServiceUrl = value; }
    }

    #endregion

    #region Fields

    /// <summary>
    /// A <typeparamref name="log4net.ILog"/> object.
    /// </summary>
    private ILog logger = Helpers.Log.Logger.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);
    /// <summary>
    /// The thread where the MainLoop runs.
    /// </summary>
    private Thread WorkerThread { get; set; }

    #endregion

    #region Constructors

    public Service() { }

    #endregion

    #region Methods

    /// <summary>
    /// Perform some actions when the service is starting or restarting.
    /// </summary>
    public void InitSystem()
    {
        try
        {
            //*********************
            //Host web services
            //*********************
            this.HostWcfServices();

            //Watch for service shutdown.
            while (!this.IsShuttingDown)
                Thread.Sleep(100);
        }
        catch (Exception ex)
        {
            this.logger.Error(ex);

            this.logger.Fatal(string.Format("Failure initiating the service. {0}", ex.Message));

            //Stop the application.
            this.Shutdown(ex);
        }
    }

    /// <summary>
    /// Shutdowns the service.
    /// </summary>
    protected virtual void Shutdown()
    {
        this.Shutdown(null);
    }
    /// <summary>
    /// Shutdowns the service.
    /// </summary>
    protected virtual void Shutdown(Exception exception)
    {
        try
        {
            //Notice other threads.
            this.IsShuttingDown = true;

            this.logger.Debug(string.Format("Stopping '{0}' service...", this.DisplayName));

            if (this.WorkerThread != null)
            {
                //Stops application working thread.
                if (Thread.CurrentThread.ManagedThreadId != this.WorkerThread.ManagedThreadId)
                {
                    this.WorkerThread.Join(10000);

                    if (this.WorkerThread.IsAlive == true)
                    {
                        this.logger.Debug("Forcing Worker Thread forced to end");

                        this.WorkerThread.Abort();

                        Thread.Sleep(2000);

                        this.WorkerThread.Join();
                    }
                }
            }

            this.logger.Debug("Main thread stopped");
        }
        catch (Exception ex)
        {
            this.logger.Error(ex);

            this.logger.Fatal(string.Format("Failure stopping the service. {0}", ex.Message));
        }

        try
        {
            this.logger.Debug(string.Format("'{0}' service stopped", this.DisplayName));
            this.logger.Info(string.Format("{0} service Shutdown", this.DisplayName));


            /* Releases all service's resources. */
            this.Dispose();
        }
        catch (Exception ex) { this.logger.Error(ex); }

        if (exception != null)
        {
            throw new Exception(string.Format(
                "The service stopped because an exceotion was threw. Exception: {0}"
                , exception.Message)
            , exception);
        }
    }
    /// <summary>
    /// Starts the service.
    /// </summary>
    public virtual void Start()
    {
        /* Starts application without arguments. */
        this.Start(new string[] { });
    }
    /// <summary>
    /// Starts the service.
    /// </summary>
    /// <param name="args">A list of starting arguments.</param>
    public virtual void Start(string[] args)
    {
        try
        {
            string configFile = string.Format("{0}{1}.config", Helpers.Common.HelperCommon.GetExecutingExePath(), AppDomain.CurrentDomain.FriendlyName);

            this.logger.Info("#############################################");
            this.logger.Info(string.Format("'{0}' starting", this.DisplayName));
            this.logger.Info("#############################################");


            //************************
            // Log conditional compilation flags
            //************************
            List<string> condCompFlagsOn = new List<string>();

#if DEBUG
            condCompFlagsOn.Add("DEBUG");
#endif
#if TRACE
            condCompFlagsOn.Add("TRACE");
#endif

            this.logger.Debug(string.Format("Compilation flags: {0}.", condCompFlagsOn.ToLog()));


            //************************
            // Load configuration
            //************************
            ServiceConfiguration config = (ServiceConfiguration)ConfigurationManager.GetSection("ServiceConfiguration");

            
            if (config != null)
            {
                this.Configuration = config;

                //Logs configuration loaded.
                this.logger.Info(string.Format("Service configuration loaded: {0}", this.Configuration));
            }
            else
            {
                throw new Exception("The service is not configured correctly.");
            }


            //Starts service Worker Thread.
            this.WorkerThread = new Thread(this.InitSystem);
            this.WorkerThread.Start();
        }
        catch (Exception ex)
        {
            this.logger.Error(ex);

            this.logger.Fatal(
                string.Format("Failure starting '{0}'. {1}", this.DisplayName, ex.Message));

            this.Shutdown(ex);
        }
    }

    public void HostWcfServices()
    {
        try
        {
            string serviceUrlEnd = this.WcfServiceUrl;
            string url = "";

            try
            {
                ServiceHost serviceHost;
                ServiceEndpoint endpoint;
                string endpointName;


                var service = new WcfService();

                url = this.BuildServiceUrl("http", this.WcfServiceHost, this.WcfServicePort, serviceUrlEnd);


                //Initialize service host.
                serviceHost = new ServiceHost(new WcfService(), new Uri[] { new Uri(url) });


                //Add web http service endpoint.
                endpointName = "";
                try
                {
                    endpoint = serviceHost.AddServiceEndpoint(
                        typeof(IWcfService)
                        , new WebHttpBinding()
                        {
                            //Enable cross domain for web access.
                            CrossDomainScriptAccessEnabled = true
                        }
                        , endpointName
                    );


                    //To add a WebHttpBinding endpoint it is necessary add to it a WebHttpBehavior to make it work.
                    //Add ModelServiceWebHttpBehavior behavior to indicate service how to serialize custom objects.
                    endpoint.Behaviors.Add(new WcfServiceWebHttpBehavior());


                    //Add serviec metadata behavior to then add a mex endpoint to allow visual studio references to the sevice.
                    serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior());

                    serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

                    //Add basic http binding to allow .Net client access.
                    serviceHost.AddServiceEndpoint(
                        typeof(IWcfService)
                        , new BasicHttpBinding()
                        {
                            BypassProxyOnLocal = true,
                        }
                        , "basic");

                    this.logger.Debug(string.Format("Service endpoint added on '{0}{1}'", url, endpointName));
                }
                catch (Exception ex)
                {
                    this.logger.Error(
                        string.Format(
                            "Error adding service endpoint on url '{0}{1}'."
                            , url
                            , endpointName
                        )
                        , ex
                    );
                }


                //Open service host.
                serviceHost.Open();


                this.logger.Debug(string.Format("Service hosted on '{0}'", url));
            }
            catch (Exception ex)
            {
                this.logger.Error(string.Format("Error hosting service on url '{0}'.", url), ex);
            }
        }
        catch (Exception ex)
        {
            this.logger.Error(ex);

            this.logger.Fatal(string.Format("Failure initiating the service. {0}", ex.Message));

            //Stops the application.
            this.Shutdown(ex);
        }
    }
    private string BuildServiceUrl(string protocol, string host, int port, string urlEnd)
    {
        string urlTemplate = "{0}://{1}:{2}/{3}/";
        string url = "";

        //If the host of the service is null or empty configure it for any ip url.
        url = string.Format(
            urlTemplate
            , protocol
            , (string.IsNullOrEmpty(host) == true)
                    ? IPAddress.Any.ToString()
                    : host
            , port
            , urlEnd
        );

        return url;
    }

    #endregion

    #region Override Methods

    /// <summary>
    /// Releases all resources used by the current instance of <typeparamref name="QualityFilesService.QualityFilesService"/>.
    /// </summary>
    public new virtual void Dispose()
    {
        /* Calls base class dispose methods. */
        base.Dispose(true);
    }
    /// <summary>
    /// Executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating
    /// system starts (for a service that starts automatically). Specifies actions to take when the service starts.
    /// </summary>
    /// <param name="args">Data passed by the start command.</param>
    protected override void OnStart(string[] args)
    {
        this.Start(args);
    }
    /// <summary>
    /// Executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to
    /// take when a service stops running.
    /// </summary>
    protected override void OnStop()
    {
        this.Shutdown();
    }
    /// <summary>
    /// Executes when a Pause command is sent to the service by the Service Control Manager (SCM). Specifies actions to
    /// take when a service pauses.
    /// </summary>
    protected override void OnPause()
    {
        this.Shutdown();
    }

    #endregion
}